Skip to content

test: real-data PVT integration test (Fraunhofer L125, GPS L1 C/A)#44

Merged
zsoerenm merged 1 commit into
masterfrom
pvt-integration-test
Jun 23, 2026
Merged

test: real-data PVT integration test (Fraunhofer L125, GPS L1 C/A)#44
zsoerenm merged 1 commit into
masterfrom
pvt-integration-test

Conversation

@zsoerenm

Copy link
Copy Markdown
Member

Summary

Adds an opt-in real-data integration test that computes a full PVT fix end to end from a real-sky GNSS recording — the real-signal counterpart to the synthetic pvt.jl / pvt_iono_tropo.jl tests. It exercises the whole JuliaGNSS stack the way a receiver does: Acquisition.jl acquires GPS L1 C/A, Tracking.jl tracks, GNSSDecoder.jl decodes the navigation message, and calc_pvt produces a position that is asserted to land at the recording site.

Modelled on the GNSSDecoder.jl / Tracking.jl Flexiband integration tests, but taken all the way to a position fix.

Capture & why GPS L1 C/A only

  • Fraunhofer IIS "L125" static roof recording (Nuremberg, 2014-09-23, 210 s, CC BY-NC), L1-band .bin (zero-IF complex baseband, fs = 20 MHz, interleaved int8 I/Q — parameters from Fraunhofer's own GNSS-SDR config).
  • calc_pvt needs ≥4 healthy satellites of the same GNSS. GPS L1 C/A is the only constellation here with ≥4 satellites that decode a complete ephemeris. (The III-7a capture that Tracking/GNSSDecoder use is only ~15.7 s — too short to decode a full ephemeris, so it can't yield a fix.)
  • The first 100 s is used: a GPS LNAV frame is 30 s and a cold start can need ~45 s to capture a consecutive subframe 1-2-3 set, so 100 s clears the ≥4-satellite bar with margin.

Streaming demux (constant memory)

Mirrors JuliaGNSS/Tracking.jl#158: the full archive is ~3.45 GB, so we range-download only a bounded compressed prefix (~2 GB) and cache that (never the decoded signal). During tracking it raw-inflates the prefix and demuxes one 0.2 s window of int8 I/Q → ComplexF32 at a time, feeding each straight into track!. Peak memory is a single window (~16 MB), not the ~16 GB a whole-span decode would need.

Assertions

Run against the real capture, the pipeline decodes 4 satellites (PRNs 13, 17, 20, 23) and fixes at 49.4873°N, 11.1255°E, 421 m (the Fraunhofer roof). The fix is fully deterministic (bit-identical single- vs multi-threaded), and the satellite set is pinned so the geometry is fixed too, so the test asserts:

  • ≥4 satellites used; the 4 known PRNs decode a healthy ephemeris,
  • position within 100 m of the decoded reference,
  • finite, positive GDOP,
  • decoded navigation time is GPS week 1811 (2014-09-23),
  • a non-null solution time.

Opt-in & CI

Gated on ENV["PVT_RUN_INTEGRATION_TEST"] — a plain ]test skips it (no download). A dedicated CI job enables it and caches the compressed prefix (actions/cache, fixed key) so repeat runs skip the download.

Verified locally: default suite passes with the integration test skipped; the integration test passes 8/8 with the fix above.

🤖 Generated with Claude Code

Opt-in integration test that computes a full PVT fix end to end from a real
GNSS recording, the real-signal counterpart to the synthetic pvt.jl tests:
Acquisition.jl acquires GPS L1 C/A, Tracking.jl tracks, GNSSDecoder.jl decodes
the navigation message, and calc_pvt produces a position that is asserted to
land at the recording site.

- Capture: Fraunhofer IIS "L125" static roof recording (Nuremberg, 2014-09-23,
  CC BY-NC). GPS L1 C/A only, as it is the sole constellation with >=4
  satellites that decode a complete ephemeris (calc_pvt needs >=4 of the same
  system); the III-7a capture used by Tracking/GNSSDecoder is too short (~15.7 s)
  to decode a full ephemeris.
- Streaming: range-download only a bounded compressed prefix of the zip (cached
  on CI via PVT_TESTDATA_DIR), then raw-inflate and demux one 0.2 s window of
  int8 I/Q at a time straight into track!, mirroring JuliaGNSS/Tracking.jl#158
  so peak memory is a single window rather than the whole decoded signal.
- Asserts the fix lands within 100 m of the (deterministic) decoded
  Fraunhofer-roof position and that the decoded time is GPS week 1811
  (2014-09-23).
- Gated on ENV["PVT_RUN_INTEGRATION_TEST"]; a plain `]test` skips it (no
  download). CI runs it on a dedicated job that caches the prefix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.77%. Comparing base (e031f5a) to head (b07b0a1).

Additional details and impacted files
@@           Coverage Diff           @@
##           master      #44   +/-   ##
=======================================
  Coverage   92.77%   92.77%           
=======================================
  Files           7        7           
  Lines         360      360           
=======================================
  Hits          334      334           
  Misses         26       26           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
master b07b0a1... master / b07b0a1...
calc_pvt/GPSL1/4sats/cold 0.0325 ± 0.0029 ms 0.0328 ± 0.0027 ms 0.99 ± 0.12
calc_pvt/GPSL1/4sats/warm 28.2 ± 4 μs 28.9 ± 4.2 μs 0.974 ± 0.2
calc_pvt/GPSL1/9sats/cold 0.0415 ± 0.00075 ms 0.0415 ± 0.00076 ms 1 ± 0.026
calc_pvt/GPSL1/9sats/warm 19.1 ± 0.56 μs 19.1 ± 0.48 μs 1 ± 0.039
calc_pvt/GalileoE1B/4sats/cold 30.8 ± 5.3 μs 31.3 ± 5.5 μs 0.987 ± 0.24
calc_pvt/GalileoE1B/4sats/warm 11.9 ± 0.36 μs 12 ± 0.38 μs 0.988 ± 0.043
calc_pvt/GalileoE1B/5sats/cold 0.032 ± 0.0045 ms 0.0323 ± 0.0047 ms 0.992 ± 0.2
calc_pvt/GalileoE1B/5sats/warm 13.3 ± 0.44 μs 13.4 ± 0.38 μs 0.993 ± 0.043
time_to_load 2.27 ± 0.018 s 2.26 ± 0.0023 s 1.01 ± 0.0078
Memory benchmarks
master b07b0a1... master / b07b0a1...
calc_pvt/GPSL1/4sats/cold 0.312 k allocs: 16.9 kB 0.312 k allocs: 16.9 kB 1
calc_pvt/GPSL1/4sats/warm 0.257 k allocs: 14 kB 0.257 k allocs: 14 kB 1
calc_pvt/GPSL1/9sats/cold 0.295 k allocs: 18.3 kB 0.295 k allocs: 18.3 kB 1
calc_pvt/GPSL1/9sats/warm 0.141 k allocs: 9.02 kB 0.141 k allocs: 9.02 kB 1
calc_pvt/GalileoE1B/4sats/cold 0.301 k allocs: 16.3 kB 0.301 k allocs: 16.3 kB 1
calc_pvt/GalileoE1B/4sats/warm 0.136 k allocs: 7.09 kB 0.136 k allocs: 7.09 kB 1
calc_pvt/GalileoE1B/5sats/cold 0.291 k allocs: 16 kB 0.291 k allocs: 16 kB 1
calc_pvt/GalileoE1B/5sats/warm 0.137 k allocs: 7.34 kB 0.137 k allocs: 7.34 kB 1
time_to_load 0.149 k allocs: 11.2 kB 0.149 k allocs: 11.2 kB 1

@zsoerenm
zsoerenm merged commit 670ef06 into master Jun 23, 2026
8 checks passed
@zsoerenm
zsoerenm deleted the pvt-integration-test branch June 23, 2026 10:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant